import os
import sys

from datetime import datetime

from invoke import task

ROOT = os.path.dirname(__file__)

CLEAN_PATTERNS = [
    "build",
    "dist",
    "cover",
    "docs/_build",
    "**/*.pyc",
    ".tox",
    "**/__pycache__",
    "reports",
    "*.egg-info",
]


def color(code):
    """A simple ANSI color wrapper factory"""
    return lambda t: "\033[{0}{1}\033[0;m".format(code, t)


green = color("1;32m")
red = color("1;31m")
blue = color("1;30m")
cyan = color("1;36m")
purple = color("1;35m")
white = color("1;39m")


def header(text):
    """Display an header"""
    print(" ".join((blue(">>"), cyan(text))))
    sys.stdout.flush()


def info(text, *args, **kwargs):
    """Display informations"""
    text = text.format(*args, **kwargs)
    print(" ".join((purple(">>>"), text)))
    sys.stdout.flush()


def success(text):
    """Display a success message"""
    print(" ".join((green(">>"), white(text))))
    sys.stdout.flush()


def error(text):
    """Display an error message"""
    print(red("✘ {0}".format(text)))
    sys.stdout.flush()


def exit(text=None, code=-1):
    if text:
        error(text)
    sys.exit(-1)


def build_args(*args):
    return " ".join(a for a in args if a)


@task
def clean(ctx):
    """Cleanup all build artifacts"""
    header(clean.__doc__)
    with ctx.cd(ROOT):
        for pattern in CLEAN_PATTERNS:
            info("Removing {0}", pattern)
            ctx.run("rm -rf {0}".format(pattern))


@task
def deps(ctx):
    """Install or update development dependencies"""
    header(deps.__doc__)
    with ctx.cd(ROOT):
        ctx.run(
            "pip install -r requirements/develop.pip -r requirements/doc.pip", pty=True
        )


@task
def demo(ctx):
    """Run the demo"""
    header(demo.__doc__)
    with ctx.cd(ROOT):
        ctx.run("python examples/todo.py")


@task
def test(ctx, profile=False):
    """Run tests suite"""
    header(test.__doc__)
    kwargs = build_args(
        "--benchmark-skip",
        "--profile" if profile else None,
    )
    with ctx.cd(ROOT):
        ctx.run("pytest {0}".format(kwargs), pty=True)


@task
def benchmark(
    ctx,
    max_time=2,
    save=False,
    compare=False,
    histogram=False,
    profile=False,
    tox=False,
):
    """Run benchmarks"""
    header(benchmark.__doc__)
    ts = datetime.now()
    kwargs = build_args(
        "--benchmark-max-time={0}".format(max_time),
        "--benchmark-autosave" if save else None,
        "--benchmark-compare" if compare else None,
        (
            "--benchmark-histogram=histograms/{0:%Y%m%d-%H%M%S}".format(ts)
            if histogram
            else None
        ),
        "--benchmark-cprofile=tottime" if profile else None,
    )
    cmd = "pytest tests/benchmarks {0}".format(kwargs)
    if tox:
        envs = ctx.run("tox -l", hide=True).stdout.splitlines()
        envs = ",".join(e for e in envs if e != "doc")
        cmd = "tox -e {envs} -- {cmd}".format(envs=envs, cmd=cmd)
    ctx.run(cmd, pty=True)


@task
def cover(ctx, html=False):
    """Run tests suite with coverage"""
    header(cover.__doc__)
    extra = "--cov-report html" if html else ""
    with ctx.cd(ROOT):
        ctx.run(
            "pytest --benchmark-skip --cov flask_restx --cov-report term --cov-report xml {0}".format(
                extra
            ),
            pty=True,
        )


@task
def tox(ctx):
    """Run tests against Python versions"""
    header(tox.__doc__)
    ctx.run("tox", pty=True)


@task
def qa(ctx):
    """Run a quality report"""
    header(qa.__doc__)
    with ctx.cd(ROOT):
        info("Ensure PyPI can render README and CHANGELOG")
        info("Building dist package")
        dist = ctx.run("python setup.py sdist", pty=True, warn=False, hide=True)
        if dist.failed:
            error("Unable to build sdist package")
            exit("Quality check failed", dist.return_code)
        readme_results = ctx.run("twine check dist/*", pty=True, warn=True, hide=True)
        if readme_results.failed:
            print(readme_results.stdout)
            error("README and/or CHANGELOG is not renderable by PyPI")
        else:
            success("README and CHANGELOG are renderable by PyPI")
    if readme_results.failed:
        exit("Quality check failed", readme_results.return_code)
    success("Quality check OK")


@task
def doc(ctx):
    """Build the documentation"""
    header(doc.__doc__)
    with ctx.cd(os.path.join(ROOT, "doc")):
        ctx.run("make html", pty=True)


@task
def assets(ctx):
    """Fetch web assets"""
    header(assets.__doc__)
    with ctx.cd(ROOT):
        ctx.run("npm install")
        ctx.run("mkdir -p flask_restx/static")
        ctx.run(
            "cp node_modules/swagger-ui-dist/{swagger-ui*.{css,js}{,.map},favicon*.png,oauth2-redirect.html} flask_restx/static"
        )
        # Until next release we need to install droid sans separately
        ctx.run(
            "cp node_modules/typeface-droid-sans/index.css flask_restx/static/droid-sans.css"
        )
        ctx.run("cp -R node_modules/typeface-droid-sans/files flask_restx/static/")


@task
def dist(ctx):
    """Package for distribution"""
    header(dist.__doc__)
    with ctx.cd(ROOT):
        ctx.run("python setup.py bdist_wheel", pty=True)


@task(clean, deps, test, doc, qa, assets, dist, default=True)
def all(ctx):
    """Run tests, reports and packaging"""
    pass
